package org.neogenesis.web.dao;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

import org.neogenesis.web.Configurations;
import org.neogenesis.web.Utils;
import org.neogenesis.web.exception.NotAnalizablePlatform;
import org.neogenesis.web.loader.MetaPlatform;
import org.neogenesis.web.loader.MetaProbe;
import org.neogenesis.web.loader.ProbesLoader;
import org.neogenesis.web.loader.ProbesLoaderSOFTFile;
import org.neogenesis.web.model.Gene;
import org.neogenesis.web.model.Platform;
import org.neogenesis.web.qualifier.DataRepository;

@SessionScoped
public class PlatformDAO implements Serializable {

	@Inject
	@DataRepository
	private EntityManager em;

	@Inject
	private NcbiDAO ncbiDAO;

	@Inject
	private GeneDAO geneDAO;

	private String uploadedFilePath;

	public PlatformDAO() {
		// TODO Auto-generated constructor stub
	}

	public void create(Platform platform) {
		em.getTransaction().begin();
		em.persist(platform);
		em.getTransaction().commit();
	}

	public Platform createFromSoftFile(Platform platform, String fileName)
			throws FileNotFoundException, IOException, NotAnalizablePlatform {
		String webAppPath = FacesContext.getCurrentInstance()
				.getExternalContext().getRealPath("/");
		
		String uploadPath = webAppPath + Configurations.uploadPath;
		uploadedFilePath = uploadPath + fileName;

		// list of probes to query
		List<MetaProbe> toQueryNCBIGeneId = new LinkedList<MetaProbe>();
		List<MetaProbe> toQueryNCBIGeneSymbol = new LinkedList<MetaProbe>();

		// get the meta platform
		//MetaPlatform mp = new MetaPlatform();
		ProbesLoader probesLoader = new ProbesLoaderSOFTFile(fileName, uploadPath);

		// path of the probes file
		String platformProbesPath = webAppPath + Configurations.platformsPath
				+ Utils.getUniqueNamePlatform(platform);
		// open the stream to the file
		BufferedWriter out = new BufferedWriter(new FileWriter(
				platformProbesPath));

		for (MetaProbe metaProbe : probesLoader) {
			Gene gene = null;
			String line = "";

			if (metaProbe.getGeneId().length() > 0) {
				gene = geneDAO.find(Integer.parseInt(metaProbe.getGeneId()));
				if (gene == null) {
					toQueryNCBIGeneId.add(metaProbe);
				} else {
					String probeId = metaProbe.getProbeId();
					int geneId = gene.getId();
					line = probeId + Configurations.FIELD_SEPARATOR + geneId
							+ "\n";
					out.write(line);
				}
			} else if (metaProbe.getGeneSymbol().length() > 0) {
				gene = geneDAO.findBySymbolAndAlias(metaProbe.getGeneSymbol());

				if (gene == null) {
					toQueryNCBIGeneSymbol.add(metaProbe);
				} else {
					String probeId = metaProbe.getProbeId();
					int geneId = gene.getId();
					line = probeId + Configurations.FIELD_SEPARATOR + geneId
							+ "\n";
					out.write(line);
				}
			}
		}

		// do queries to NCBI
		ncbiDAO.setBatchSize(1000);
		// load the genes in the metarows
		ncbiDAO.retrieveGenesById(toQueryNCBIGeneId);
		ncbiDAO.retrieveGenesBySymbol(toQueryNCBIGeneSymbol);

		for (MetaProbe metaProbe : toQueryNCBIGeneId) {
			String line = "";
			Gene gene = metaProbe.getGene();

			if (gene == null)
				continue;

			geneDAO.create(metaProbe.getGene());
			String probeId = metaProbe.getProbeId();
			int geneId = gene.getId();
			line = probeId + Configurations.FIELD_SEPARATOR + geneId + "\n";
			out.write(line);
		}

		for (MetaProbe metaProbe : toQueryNCBIGeneSymbol) {
			String line = "";
			Gene gene = metaProbe.getGene();

			if (gene == null)
				continue;

			String probeId = metaProbe.getProbeId();
			int geneId = gene.getId();
			line = probeId + Configurations.FIELD_SEPARATOR + geneId + "\n";
			out.write(line);
		}
		out.flush();
		out.close();
		
		platform.setProbeFile(platformProbesPath);

		create(platform);
		Utils.deleteFile(uploadedFilePath);
		
		return platform;
	}

	
	public Platform findByName(String name) {

		Query q = em.createQuery(
				"SELECT p FROM Platform p WHERE p.accessionName = :name",
				Platform.class);
		q.setParameter("name", name);

		List<Platform> list = q.getResultList();

		if (list.size() > 0) {
			return list.get(0);
		}

		return null;
	}

	public Map<String, Integer> getMapProbeIdGeneId(Platform platform)
			throws IOException {

		String probeFiles = platform.getProbeFile();
		Map<String, Integer> map = new HashMap<String, Integer>();

		BufferedReader in = new BufferedReader(new FileReader(probeFiles));
		String line = null;

		while ((line = in.readLine()) != null) {
			String split[] = line.split(Configurations.FIELD_SEPARATOR);
			String probeId = split[0];
			int geneId = Integer.parseInt(split[1]);
			map.put(probeId, geneId);
		}

		in.close();

		return map;
	}

	public void edit(Platform platform) {
		em.merge(platform);
	}

	public void remove(Platform platform) {
		em.getTransaction().begin();
		em.remove(em.merge(platform));
		em.getTransaction().commit();
	}

	public Platform find(Object id) {
		return em.find(Platform.class, id);
	}

	public List<Platform> findAll() {
		CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
		cq.select(cq.from(Platform.class));
		return em.createQuery(cq).getResultList();
	}

	public List<Platform> findRange(int[] range) {
		CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
		cq.select(cq.from(Platform.class));
		javax.persistence.Query q = em.createQuery(cq);
		q.setMaxResults(range[1] - range[0]);
		q.setFirstResult(range[0]);
		return q.getResultList();
	}

	public int count() {
		javax.persistence.criteria.CriteriaQuery cq = em.getCriteriaBuilder()
				.createQuery();
		javax.persistence.criteria.Root<Platform> rt = cq.from(Platform.class);
		cq.select(em.getCriteriaBuilder().count(rt));
		javax.persistence.Query q = em.createQuery(cq);
		return ((Long) q.getSingleResult()).intValue();
	}
}
